Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/三件套/JS/JavaScript

02 类型系统与类型转换

14 分钟阅读 · Note

目录树 578 篇

            • 00 JavaScript学习地图
            • 01 JavaScript基础
            • 02 类型系统与类型转换
            • 03 Number与String
            • 04 Array数组
            • 05 Object对象
            • 06 集合与迭代
            • 07 函数、作用域与this
            • 08 原型、构造函数与类
            • 09 Generator与异步迭代
            • 10 异步编程
            • 11 浏览器与DOM
            • 12 二进制与文件
            • 13 标准内建对象
            • 14 模块化
            • 15 错误处理
            • 16 JSDoc
            • 90 JavaScript知识点总目录
            • 91 JavaScript修正与补充记录
          • 前端模块化
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗00 JavaScript学习地图反向链接↗90 JavaScript知识点总目录反向链接↗01 JavaScript基础同一路径↗03 Number与String同一路径↗04 Array数组同一路径↗05 Object对象同一路径
  • 02 类型系统与类型转换

02 类型系统与类型转换

> Last Format Time:8/11/2026 21:45:36

本笔记由两个 JavaScript 教程目录中的同主题内容合并而成;全部旧知识保留在“旧笔记知识全集(按来源保留)”中,并由导航逐项索引。


当前前端开发关键要点

> [!important] 学习优先级:P0 > - 区分原始类型与对象类型,理解赋值、参数传递和比较时的值语义与引用语义。 > - 掌握 typeof、instanceof、Array.isArray 与 Object.prototype.toString.call 的适用边界。 > - 警惕隐式类型转换、NaN、null 与 undefined;业务判断优先写出明确意图。 > - TypeScript 只能提供静态约束,接口响应、表单和存储数据仍需要运行时校验。


知识点导航

[现代JS教程与阮一峰ES6 · 1数据类型与变量/数据类型与转换](./02 类型系统与类型转换.md)

  • 数据类型与转换
  • 数据类型
  • 原始类型(Primitive,7 种)
  • 对象类型(Object)
  • 类型判断
  • typeof 运算符
  • instanceof
  • Object.prototype.toString.call()
  • 其他辅助方法
  • 原始类型的"包装对象"
  • 类型转换
  • 显式转换
  • 隐式转换(自动发生)

[廖雪峰教程 · 3快速入门/3.2 数据类型与变量](./02 类型系统与类型转换.md)

  • 数据类型与变量
  • 数据类型
  • Number
  • 字符串
  • 布尔值
  • 比较运算符
  • BigInt
  • null和undefined
  • 数组
  • 对象
  • 变量
  • strict模式

现代前端补充与纠错

> [!info] 修改标记 > - 【修正】:旧教程中错误、过时或容易误导的内容。 > - 【补充】:旧教程未覆盖、但当前前端开发需要掌握的内容。 > - 【修正代码】:替换或校正了旧代码示例。

> [!warning] 下方保留历史原文;类型判断和隐式转换结论以本节为准。

可靠的类型判断

  • 【修正】 typeof null === "object" 是历史遗留;数组用 Array.isArray,日期等内建对象可结合品牌检查或显式 API。
  • 【修正】 instanceof 沿原型链判断,跨 iframe/realm、手动改原型或自定义 Symbol.hasInstance 时可能不符合直觉。
  • 【修正】 Object.is 与 === 的主要差别是 NaN 和正负零;业务判断默认优先 ===,只有确实关心这些边界时用 Object.is。
  • 【补充】 BigInt 不能与 Number 直接参与算术;JSON 原生也不能序列化 BigInt,需要约定字符串或自定义转换。

转换原则

  • 【补充】 避免依赖复杂的 == 强制转换;表单、URL、存储中的值通常都是字符串,应在数据边界显式解析并校验。
  • 【补充】 Boolean("false") 为 true,因为非空字符串都是真值;不要把文本直接当布尔值使用。
  • 【修正】 Number(value) 要求整体可转换,parseInt/parseFloat 会读取合法前缀,三者不是等价 API。

旧笔记知识全集(按来源保留)

现代JS教程与阮一峰ES6 · 1数据类型与变量/数据类型与转换

数据类型与转换

现代 JS 教程 — 数据类型 MDN — JavaScript 数据类型和数据结构 MDN — typeof

JavaScript 是弱类型(动态类型)语言:变量本身没有固定类型,赋值时才确定;运行时可自动或手动把一种类型转成另一种类型。本篇是「1 数据类型与变量」章节的总览,深入内容见子笔记:String | Number | Object | Arrray | var let const。

数据类型

JS 的值不是「原始类型」就是「对象」,共 8 种类型。其中原始类型 7 种、对象类型 1 种(但对象有多种形态)。

原始类型(Primitive,7 种)
类型说明字面量示例
string字符串'hello'、"world"、`template`
number数字(整数 + 浮点数)123、3.14、NaN、Infinity
bigint任意长度的整数123n、0n
boolean布尔true、false
null"无 / 空 / 值未知" 的特殊值null
undefined"未被赋值" 的特殊值undefined
symbol唯一且不可变的标识符Symbol('id')

补充说明:

  • bigint 在数字末尾加 n,用于超出 Number.MAX_SAFE_INTEGER(2⁵³−1)的整数场景,避免浮点精度丢失。
  • symbol 常用于对象的「唯一属性键」,避免与已有键名冲突。
  • null 与 undefined 是两个独立类型:null 是"已知为空",undefined 是"尚未赋值"。
对象类型(Object)

对象是键值对的集合,可存储多个值作为属性。除了普通对象 {},还有多种"形态"——它们本质都是对象,但各自有专用语法和 API:

  • Array 数组:用 [] 创建,索引化的对象
  • Function 函数:可调用的对象
  • 内建对象:Date、RegExp、Map、Set、Error、Promise 等

> 栈 vs 堆:原始类型值直接存在栈里;对象存在堆里,变量持有的是"指向堆的引用"(地址),所以对象叫"引用类型"。这解释了为什么对象赋值/传参是"共享同一份",而原始类型是"拷贝一份"。

let a = { x: 1 };
let b = a;       // b 拿到的是同一个引用
b.x = 2;
console.log(a.x); // 2 —— a 也被改了

let p = 1;
let q = p;       // q 拷贝了一份值
q = 2;
console.log(p);   // 1 —— p 不受影响
类型判断
typeof 运算符

注意 typeof 是操作符不是函数。括号与 typeof 本身无关,写不写都行:

typeof operand       // 推荐写法(操作符风格)
typeof(operand)      // 括号只是数学分组,不是函数调用
typeof 123;        // 'number'
typeof 123n;       // 'bigint'
typeof NaN;        // 'number'
typeof 'str';      // 'string'
typeof true;       // 'boolean'
typeof undefined;  // 'undefined'
typeof Symbol();   // 'symbol'
typeof Math.abs;   // 'function'

null与部分引用类型全是object:

typeof null;       // 'object'   ← 官方承认的历史 bug
typeof [];         // 'object'   ← 数组本质是对象
typeof {};         // 'object'
  1. typeof null === 'object':1995 年初代 JS 留下的 bug,沿用至今。永远不会修复——修了会破坏现有代码。判断 null 必须显式 x === null。
  2. typeof [] === 'object':数组的原型链是 arr → Array.prototype → Object.prototype → null,所以 typeof 把它当成对象。要判断数组必须用 Array.isArray(x) 或 x instanceof Array。
instanceof

用于检测一个对象是否是某个构造函数的实例。它的底层原理是判断构造函数的 prototype 属性是否出现在该对象的原型链上。

维度typeofinstanceof
适合判断原始类型引用类型(对象、数组、自定义类)
原理操作符内部硬编码类型字符串沿原型链查找构造函数
对 null误判 'object'抛错或返回 false
对 []返回 'object'arr instanceof Array === true
跨 iframe不受影响失效(不同 realm 的原型不同)
const arr = [1, 2];
typeof arr;           // 'object'
arr instanceof Array; // true
Array.isArray(arr);   // true(推荐)

经验法则:原始类型用 typeof,引用类型用 instanceof / Array.isArray。

Object.prototype.toString.call()

这是目前最通用、最可靠的类型判断“终极方案”。它通过调用对象内部的 <websource>Class</websource> 属性,能够精准识别所有内置类型15。 适用场景:任何数据类型的精确判断,包括 null、undefined 和各类引用类型。

Object.prototype.toString.call('hello');     // '[object String]'
Object.prototype.toString.call(123);         // '[object Number]'
Object.prototype.toString.call(null);        // '[object Null]'
Object.prototype.toString.call(undefined);   // '[object Undefined]'
Object.prototype.toString.call([]);          // '[object Array]'
Object.prototype.toString.call(new Date());  // '[object Date]'

封装使用:

function getType(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1);
}
其他辅助方法
  • Array.isArray()
  • 判断构造函数
原始类型的"包装对象"

为什么 'hello'.toUpperCase() 能工作?字符串明明是原始类型,却能调用方法?

原因是 JS 在访问原始类型的属性或方法时,临时创建一个"包装对象",调用完立即销毁。原始类型本身仍然是原始的,没有属性。

每种原始类型都有自己的包装构造函数:

原始类型包装构造函数示例
stringString'abc'.length → 临时 String 对象
numberNumber(123).toFixed(2)
bigintBigInt(10n).toString()
booleanBooleantrue.toString()
symbolSymbolSymbol('x').description

> null 和 undefined 没有包装对象,所以访问它们的属性会直接报错。

'hello'.toUpperCase(); // 'HELLO' —— 内部临时 new String('hello').toUpperCase()
null.toString();       // ❌ TypeError

这种"用完即弃"的机制让原始类型既轻量(只存一个值)又方便(能用方法),是 JS 类型系统的设计妥协。

类型转换

大多数情况下,运算符和函数会自动把值转成正确的类型:alert 转字符串显示、算术运算符转数字。某些场景下需要显式转换来确保正确行为。

显式转换
目标函数说明
字符串String(x)任何值都能转,副作用为 undefined → 'undefined'、null → 'null'
数字Number(x)解析失败返回 NaN;null → 0、undefined → NaN
布尔Boolean(x)只有 6 个值转 false,其余全是 true
String(123);         // '123'
String(null);        // 'null'
Number('123abc');    // NaN
Number(null);        // 0
Number(undefined);   // NaN
Boolean(0);          // false
Boolean([]);         // true(注意:空数组是真值)

false、0、''、null、undefined、NaN其余全是 true——包括 []、{}、'0'、' '(带空格的非空字符串)。

Boolean([]);          // true
Boolean({});          // true
Boolean('0');         // true
Boolean(' ');         // true
Boolean('false');     // true
隐式转换(自动发生)

JS 在这些场景会自动转换类型:

  • 算术运算 + - * / %:+ 遇字符串则全部转字符串,其余运算符全部转数字
  • 比较运算 == &gt; &lt;:== 会做类型转换,=== 不会
  • 逻辑运算 && || !:内部会转布尔再判断
1 + '2';        // '12'(+ 遇字符串)
'5' - 3;        // 2(- 转数字)
true + 1;       // 2

== 会做隐式转换,规则繁杂,面试常考。开发一律用 ===,从源头规避。

  • == 比较中,null / undefined 只跟彼此相等,不和任何数字或字符串相等
  • NaN 永远不等于自身,要判断 NaN 用 Number.isNaN(x) 或 x !== x
  • 对象与对象比较的是引用地址,内容相同但地址不同就不等
false == 0;       // true
false == '';      // true
0 == '';          // true

null == undefined;// true(唯一一组)
null == 0;        // false
undefined == 0;   // false

NaN == NaN;       // false(NaN 永远不等于自身)
[] == ![];        // true([] == false → 0 == 0)
{} == {};         // false(引用比较地址,不同对象地址不同)

廖雪峰教程 · 3快速入门/3.2 数据类型与变量

数据类型与变量
数据类型

计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是,计算机能处理的远不止数值,还可以处理文本、图形、音频、视频、网页等各种各样的数据,不同的数据,需要定义不同的数据类型。在JavaScript中定义了以下几种数据类型,基本数据类型共有7种(ES6):

Number

JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型:

123; // 整数123
0.456; // 浮点数0.456
1.2345e3; // 科学计数法表示1.2345x1000,等同于1234.5
-99; // 负数
NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示
Infinity; // Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity

计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用0x前缀和0-9,a-f表示,例如:0xff00,0xa5b4c3d2,等等,它们和十进制表示的数值完全一样。

Number可以直接做四则运算,规则和数学一致:

1 + 2; // 3
(1 + 2) * 5 / 2; // 7.5
2 / 0; // Infinity
0 / 0; // NaN
10 % 3; // 1
10.5 % 3; // 1.5

注意%是求余运算。

要注意,JavaScript的Number不区分整数和浮点数,也就是说,12.00 === 12。(在大多数其他语言中,整数和浮点数不能直接比较)并且,JavaScript的整数最大范围不是±263,而是±253,因此,超过253的整数就可能无法精确表示:

字符串

字符串是以单引号'或双引号"括起来的任意文本,比如'abc',"xyz"等等。请注意,''或""本身只是一种表示方式,不是字符串的一部分,因此,字符串'abc'只有a,b,c这3个字符。

布尔值

布尔值和布尔代数的表示完全一致,一个布尔值只有true、false两种值,要么是true,要么是false,可以直接用true、false表示布尔值,也可以通过布尔运算计算出来:

true; // 这是一个true值
false; // 这是一个false值
2 > 1; // 这是一个true值
2 >= 3; // 这是一个false值

&&运算是与运算,只有所有都为true,&&运算结果才是true:

true && true; // 这个&&语句计算结果为true
true && false; // 这个&&语句计算结果为false
false && true && false; // 这个&&语句计算结果为false

||运算是或运算,只要其中有一个为true,||运算结果就是true:

false || false; // 这个||语句计算结果为false
true || false; // 这个||语句计算结果为true
false || true || false; // 这个||语句计算结果为true

!运算是非运算,它是一个单目运算符,把true变成false,false变成true:

! true; // 结果为false
! false; // 结果为true
! (2 > 5); // 结果为true

布尔值经常用在条件判断中,比如

var age = 15;
if (age >= 18) {
    alert('adult');
} else {
    alert('teenager');
}
比较运算符

当我们对Number做比较时,可以通过比较运算符得到一个布尔值:

2 > 5; // false
5 >= 2; // true
7 == 7; // true

实际上,JavaScript允许对任意数据类型做比较:

false == 0; // true
false === 0; // false

要特别注意相等运算符==。JavaScript在设计时,有两种比较运算符: 第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果; 第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。

由于JavaScript这个设计缺陷,_不要_使用==比较,始终坚持使用===比较。 另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:

NaN === NaN; // false

唯一能判断NaN的方法是通过isNaN()函数:

isNaN(NaN); // true

最后要注意浮点数的相等比较:

1 / 3 === (1 - 2 / 3); // false

这不是JavaScript的设计缺陷。浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:

Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
BigInt

要精确表示比253还大的整数,可以使用内置的BigInt类型,它的表示方法是在整数后加一个n,例如9223372036854775808n,也可以使用BigInt()把Number和字符串转换成BigInt 使用BigInt可以正常进行加减乘除等运算,结果仍然是一个BigInt,但不能把一个BigInt和一个Number放在一起运算:

null和undefined

null表示一个“空”的值,它和0以及空字符串''不同,0是一个数值,''表示长度为0的字符串,而null表示“空”。

在其他语言中,也有类似JavaScript的null的表示,例如Java也用null,Swift用nil,Python用None表示。但是,在JavaScript中,还有一个和null类似的undefined,它表示“未定义”。

JavaScript的设计者希望用null表示一个空的值,而undefined表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。

数组

数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型。例如:

[1, 2, 3.14, 'Hello', null, true];

上述数组包含6个元素。数组用[]表示,元素之间用,分隔。

另一种创建数组的方法是通过Array()函数实现:

new Array(1, 2, 3); // 创建了数组[1, 2, 3]

然而,出于代码的可读性考虑,强烈建议直接使用[]。

对象

JavaScript的对象是一组由键-值组成的无序集合,例如:

var person = {
    name: 'Bob',
    age: 20,
    tags: ['js', 'web', 'mobile'],
    city: 'Beijing',
    hasCar: true,
    zipcode: null
};

JavaScript对象的键都是字符串类型,值可以是任意数据类型。上述person对象一共定义了6个键值对,其中每个键又称为对象的属性,例如,person的name属性为'Bob',zipcode属性为null。

要获取一个对象的属性,我们用对象变量.属性名的方式:

person.name; // 'Bob'
person.zipcode; // null
变量

变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型。

变量在JavaScript中就是用一个变量名表示,变量名是大小写英文、数字、$和_的组合,且不能用数字开头。变量名也不能是JavaScript的关键字,如if、while等。申明一个变量用var语句,比如:

var a; // 申明了变量a,此时a的值为undefined
var $b = 1; // 申明了变量$b,同时给$b赋值,此时$b的值为1
var s_007 = '007'; // s_007是一个字符串
var Answer = true; // Answer是一个布尔值true
var t = null; // t的值是null

变量名也可以用中文,但是,请不要给自己找麻烦。

在JavaScript中,使用等号=对变量进行赋值。可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var申明一次,例如:

var a = 123; // a的值是整数123
a = 'ABC'; // a变为字符串

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言,赋值语句如下:

int a = 123; // a是整数类型变量,类型用int申明
a = "ABC"; // 错误:不能把字符串赋给整型变量

和静态语言相比,动态语言更灵活,就是这个原因。

请不要把赋值语句的等号等同于数学的等号。比如下面的代码:

var x = 10;
x = x + 2;

如果从数学上理解x = x + 2那无论如何是不成立的,在程序中,赋值语句先计算右侧的表达式x + 2,得到结果12,再赋给变量x。由于x之前的值是10,重新赋值后,x的值变成12。

strict模式

JavaScript在设计之初,为了方便初学者学习,并不强制要求用var申明变量。这个设计错误带来了严重的后果:如果一个变量没有通过var申明就被使用,那么该变量就自动被申明为全局变量:

i = 10; // i现在是全局变量

在同一个页面的不同的JavaScript文件中,如果都不用var申明,恰好都使用了变量i,将造成变量i互相影响,产生难以调试的错误结果。

使用var申明的变量则不是全局变量,它的范围被限制在该变量被申明的函数体内(函数的概念将稍后讲解),同名变量在不同的函数体内互不冲突。

为了修补JavaScript这一严重设计缺陷,ECMA在后续规范中推出了strict模式,在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。

启用strict模式的方法是在JavaScript代码的第一行写上:

'use strict';

这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。

不用var申明的变量会被视为全局变量,为了避免这一缺陷,所有的JavaScript代码都应该使用strict模式。我们在后面编写的JavaScript代码将全部采用strict模式。

另一种申明变量的方式是let,这也是现代JavaScript推荐的方式